﻿using Common;
using Common.Extensions;
using System.Diagnostics;
using System.Net;
using System.Net.Sockets;

namespace NewDevice
{
    /// <summary>
    /// IO模块类型
    /// </summary>
    public enum IoModuleType
    {
        /// <summary>
        /// 只有输出的IO板
        /// </summary>
        OnlyDi = 0x01,

        /// <summary>
        /// 只有输出的IO板
        /// </summary>
        OnlyDo = 0x10,

        /// <summary>
        /// 包含输入输出的IO板
        /// </summary>
        All = 0x11
    }

    /// <summary>
    /// DIChange 类
    /// </summary>
    public class DiChanged(Action diChangedEvent)
    {
        /// <summary>
        /// DI index
        /// </summary>
        public int DIndex { get; set; }

        /// <summary>
        /// changed condition value
        /// </summary>
        public bool ConditionValue { get; set; }

        /// <summary>
        /// 执行委托
        /// </summary>
        public Action DiChangedEvent { get; set; } = diChangedEvent;
    }

    /// <summary>
    /// IO模块
    /// </summary>
    public class IoModule
    {
        private readonly CancellationTokenSource _cancellationTokenSource = new();

        /// <summary>
        /// 释放资源
        /// </summary>
        public void Dispose()
        {
            _cancellationTokenSource.Cancel();
            _disposing = true;
            Task.WaitAll(_refreshTask);
            _client?.Close();
        }

        /// <summary>
        /// IO模块类型
        /// </summary>
        public IoModuleType IoModuleType { get; set; } = IoModuleType.All;

        /// <summary>
        /// DI变化执行委托列表
        /// </summary>
        public List<DiChanged> ListDiChanged { get; set; } = new List<DiChanged>();

        private Socket _client = null!;
        private readonly Lock _objLock = new Lock();
        private bool _disposing = false;
        private bool _connected = false;
        private Task _refreshTask = null!;

        /// <summary>
        /// 设备连接
        /// </summary>
        /// <param name="ip"> </param>
        /// <exception cref="Exception"> </exception>
        [Conditional("DEVICE")]
        public void Connect(string ip = "192.168.1.12")
        {
            if (_connected)
                return;
            var port = 502;
            _client = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);//创建Socket
            ManualResetEventSlim openCompletedEvent = new ManualResetEventSlim(false);
            Task.Run(() =>
            {
                _client.Connect(IPAddress.Parse(ip), port);
                openCompletedEvent.Set();
            }, _cancellationTokenSource.Token);
            if (!openCompletedEvent.Wait(2000))
            {
                throw new Exception($"IOModule,IP=[{ip}]连接超时");
            }
            _refreshTask = Task.Run(() =>
            {
                while (!_disposing)
                {
                    RefreshIoValue();
                }
            }, _cancellationTokenSource.Token);
            Thread.Sleep(200);  //等待监控线程执行
            _connected = true;
        }

        private void RefreshIoValue()
        {
            if ((((int)IoModuleType) & 0x01) > 0)
            {
                ReadDiValue();
                Thread.Sleep(50);
                foreach (var item in ListDiChanged)
                {
                    if (DiValue.Index(item.DIndex) == item.ConditionValue)
                    {
                        item.DiChangedEvent?.Invoke();
                    }
                }
            }

            if ((((int)IoModuleType) & 0x10) > 0)
            {
                ReadDoValue();
                Thread.Sleep(50);
            }
        }

        private readonly byte[] _cmdReadDoValue = [0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x01, 0x01, 0x00, 0x00, 0x00, 0x20
        ];

        private readonly byte[] _cmdReadDiValue = [0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x01, 0x02, 0x00, 0x00, 0x00, 0x20
        ];

        private void ReadDiValue()
        {
            var data = SendCommand(_cmdReadDiValue);
            if (!CheckReceivedData(data, 0x02, [0x00, 0x07]))
            {
                MyLog.Error(new Exception($"read di value error, receive[{data}]"));
            }
            DiValue = [data[9], data[10], data[11], data[12]];
        }

        /// <summary>
        /// 设置DO值
        /// </summary>
        /// <param name="value"> </param>
        public void SetDoValue(byte value)
        {
            byte[] cmd = { 0x00, 0x00, 0x00, 0x00,
             0x00, 0x08, 0x01, 0x0f,
             0x00, 0x00, 0x00, 0x08,
             0x01,value };
            var data = SendCommand(cmd);
            if (!CheckReceivedData(data, 0x0f, [0x00, 0x06]))
            {
                MyLog.Error(new Exception($"set do value error,received data:[{data}]"));
            }
        }

        /// <summary>
        /// 设置DO值
        /// </summary>
        /// <param name="index"> </param>
        /// <param name="value"> </param>
        /// <exception cref="Exception"> </exception>
        public void SetDoValue(int index, int value)
        {
            int doIndex = (int)index;
            byte[] cmd = { 0x00, 0x00, 0x00, 0x00,
             0x00, 0x06, 0x01, 0x05,
             0x00, Convert.ToByte(doIndex), (value==0)?(byte)0x00:(byte)0xff, 0x00};
            var data = SendCommand(cmd);
            if (!CheckReceivedData(data, 0x05, [0x00, 0x06]))
            {
                throw new Exception($"set do value error,received data:[{data}]");
            }
        }

        /// <summary>
        /// 读取DO值
        /// </summary>
        public void ReadDoValue()
        {
            var data = SendCommand(_cmdReadDoValue);
            if (!CheckReceivedData(data, 0x01, [0x00, 0x07]))
            {
                MyLog.Error(new Exception($"read do value error, receive[{data}]"));
            }
            DoValue = [data[9], data[10], data[11], data[12]];
        }

        private byte[] SendCommand(byte[] cmd)
        {
            lock (_objLock)
            {
                _client.Send(cmd, cmd.Length, SocketFlags.None);

                byte[] recvBytes = new byte[1024];

                int actualBytes = _client.Receive(recvBytes, 0, recvBytes.Length, SocketFlags.None);
                return recvBytes;
            }
        }

        private bool CheckReceivedData(byte[] data, byte funcCode, byte[] dataLen)
        {
            for (int i = 0; i < 4; i++)
            {
                if (data[i] != 0x00)
                {
                    return false;
                }
            }
            for (int i = 0; i < 2; i++)
            {
                if (data[4 + i] != dataLen[i])
                {
                    return false;
                }
            }
            if (data[6] != 0x01)
            {
                return false;
            }
            if (data[7] != funcCode)
            {
                return false;
            }
            return true;
        }

        private byte[] DiValue { get; set; } = null!;

        private byte[] DoValue { get; set; } = null!;

        /// <summary>
        /// 获取DI状态
        /// </summary>
        /// <param name="index"> </param>
        /// <returns> </returns>
        public bool GetDiValue(int index)
        {
            if (!_connected)
                return false;
            return DiValue.Index(index);
        }

        /// <summary>
        /// 获取DO状态
        /// </summary>
        /// <param name="index"> DO索引 </param>
        /// <returns> </returns>
        public bool GetDoValue(int index)
        {
            if (!_connected)
                return false;
            return DoValue.Index(index);
        }
    }
}